using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Markup.Primitives;

namespace AvalonDock
{
    public class DocumentPane : Pane
    {
        #region Constants and Fields

        /// <summary>
        /// ContainsActiveDocument Read-Only Dependency Property
        /// </summary>
        /// <remarks>This property is especially intended for use in restyling.</remarks>
        private static readonly DependencyPropertyKey ContainsActiveDocumentPropertyKey =
            DependencyProperty.RegisterReadOnly(
                "ContainsActiveDocument",
                typeof(bool),
                typeof(DocumentPane),
                new FrameworkPropertyMetadata(false, OnContainsActiveDocumentChanged));

        public static readonly DependencyProperty ContainsActiveDocumentProperty =
            ContainsActiveDocumentPropertyKey.DependencyProperty;

        private Button _optionsContextMenuPlacementTarget;

        #endregion

        #region Constructors and Destructors

        static DocumentPane()
        {
            //This OverrideMetadata call tells the system that this element wants to provide a style that is different than its base class.
            //This style is defined in themes\generic.xaml
            DefaultStyleKeyProperty.OverrideMetadata(
                typeof(DocumentPane), new FrameworkPropertyMetadata(typeof(DocumentPane)));
        }

        public DocumentPane()
        {
            Loaded += DocumentPane_Loaded;
            PreviewDrop += OnPreviewDrop;
            PreviewMouseDown +=
                (sender, args) =>
                    {
                        GetManager().DisableHideFlyoutWindow = false;
                        GetManager().HideFlyoutWindow();
                    };
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets the ContainsActiveDocument property.  This dependency property 
        /// indicates if this pane contains a <see cref="DocumentPane"/> that has <see cref="ManagedContent.IsActiveDocument"/> property set to true.   
        /// </summary>
        public bool ContainsActiveDocument
        {
            get
            {
                return (bool)GetValue(ContainsActiveDocumentProperty);
            }
        }

        /// <summary>
        /// Returns a value indicating if this pane is the main document pane
        /// </summary>
        /// <remarks>The main document pane is the default pane that remains always visible.</remarks>
        public bool? IsMainDocumentPane
        {
            get
            {
                if (GetManager() == null)
                {
                    return null;
                }

                return GetManager().MainDocumentPane == this;
            }
        }

        #endregion

        #region Properties

        protected override bool IsSurfaceVisible
        {
            get
            {
                if (IsMainDocumentPane.HasValue && !IsMainDocumentPane.Value && Items.Count == 0)
                {
                    return false;
                }

                return true;
            }
        }

        #endregion

        #region Public Methods and Operators

        public static List<DependencyProperty> GetAttachedProperties(Object element)
        {
            var attachedProperties = new List<DependencyProperty>();
            MarkupObject markupObject = MarkupWriter.GetMarkupObjectFor(element);
            if (markupObject != null)
            {
                foreach (MarkupProperty mp in markupObject.Properties)
                {
                    if (mp.IsAttached)
                    {
                        attachedProperties.Add(mp.DependencyProperty);
                    }
                }
            }
            return attachedProperties;
        }

        public static List<DependencyProperty> GetDependencyProperties(Object element)
        {
            var properties = new List<DependencyProperty>();
            MarkupObject markupObject = MarkupWriter.GetMarkupObjectFor(element);
            if (markupObject != null)
            {
                foreach (MarkupProperty mp in markupObject.Properties)
                {
                    if (mp.DependencyProperty != null)
                    {
                        properties.Add(mp.DependencyProperty);
                    }
                }
            }
            return properties;
        }

        public void Close()
        {
            CloseThis(null);
        }

        public DocumentPane CreateNewHorizontalTabGroup()
        {
            var activeContent = (ManagedContent)SelectedItem;
            var oldContainerPane = (DocumentPane)activeContent.ContainerPane;
            var newContainerPane = new DocumentPane();

            oldContainerPane.RemoveContent(activeContent);
            newContainerPane.Items.Add(activeContent);

            GetManager().Anchor(newContainerPane, this, AnchorStyle.Bottom);

            activeContent.Activate();
            newContainerPane.RefreshContainsActiveContentProperty();
            newContainerPane.RefreshContainsActiveDocumentProperty();
            oldContainerPane.RefreshContainsActiveContentProperty();
            oldContainerPane.RefreshContainsActiveDocumentProperty();

            return newContainerPane;
        }

        public DocumentPane CreateNewVerticalTabGroup()
        {
            var activeContent = (ManagedContent)SelectedItem;
            var oldContainerPane = (DocumentPane)activeContent.ContainerPane;
            var newContainerPane = new DocumentPane();

            oldContainerPane.RemoveContent(activeContent);
            newContainerPane.Items.Add(activeContent);

            GetManager().Anchor(newContainerPane, this, AnchorStyle.Right);

            activeContent.Activate();
            newContainerPane.RefreshContainsActiveContentProperty();
            newContainerPane.RefreshContainsActiveDocumentProperty();
            oldContainerPane.RefreshContainsActiveContentProperty();
            oldContainerPane.RefreshContainsActiveDocumentProperty();

            return newContainerPane;
        }

        public override void OnApplyTemplate()
        {
            _optionsContextMenuPlacementTarget = GetTemplateChild("PART_ShowContextMenuButton") as Button;

            if (_optionsContextMenuPlacementTarget != null)
            {
                _optionsContextMenuPlacementTarget.Click += (s, e) => { ShowDocumentsListMenu(); };
            }

            base.OnApplyTemplate();
        }

        public override bool OpenOptionsMenu(UIElement menuTarget)
        {
            if (cxOptions == null)
            {
                cxOptions =
                    TryFindResource(new ComponentResourceKey(typeof(DockingManager), ContextMenuElement.DocumentPane))
                    as ContextMenu;
            }

            return base.OpenOptionsMenu(menuTarget);
        }

        #endregion

        #region Methods

        internal void CheckContentsEmpty()
        {
            if (Items.Count == 0)
            {
                bool isMainDocPaneToBeClosed = IsMainDocumentPane.HasValue && IsMainDocumentPane.Value;

                if (isMainDocPaneToBeClosed)
                {
                    DockingManager manager = GetManager();
                    var candidateNewMainDocPane = manager.FindAnotherLogicalChildContained<DocumentPane>(this);
                    if (candidateNewMainDocPane != null && candidateNewMainDocPane.GetManager() == GetManager())
                    {
                        var containerPanel = Parent as ResizingPanel;
                        if (containerPanel != null)
                        {
                            containerPanel.RemoveChild(this);
                        }

                        manager.MainDocumentPane = candidateNewMainDocPane;
                        candidateNewMainDocPane.NotifyPropertyChanged("IsMainDocumentPane");
                    }
                }
                else
                {
                    var containerPanel = Parent as ResizingPanel;
                    if (containerPanel != null)
                    {
                        containerPanel.RemoveChild(this);
                    }
                }
            }
        }

        internal override ResizingPanel GetContainerPanel()
        {
            return GetParentDocumentPaneResizingPanel();
        }

        internal DocumentPaneResizingPanel GetParentDocumentPaneResizingPanel()
        {
            var parentPanel = LogicalTreeHelper.GetParent(this) as ResizingPanel;

            if (parentPanel == null)
            {
                return null;
            }

            while (!(parentPanel is DocumentPaneResizingPanel))
            {
                parentPanel = LogicalTreeHelper.GetParent(parentPanel) as ResizingPanel;

                if (parentPanel == null)
                {
                    return null;
                }
            }

            return parentPanel as DocumentPaneResizingPanel;
        }

        internal void RefreshContainsActiveDocumentProperty()
        {
            SetContainsActiveDocument(Items.Cast<ManagedContent>().FirstOrDefault(d => d.IsActiveDocument) != null);

            if (Items.Count > 0)
            {
                Debug.WriteLine(
                    string.Format(
                        "{0} ContainsActiveDocument ={1}", ((ManagedContent)Items[0]).Title, ContainsActiveDocument));
            }
        }

        protected override void OnCanExecuteCommand(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = GetManager() != null;

            if (e.CanExecute)
            {
                if (e.Command == DocumentPaneCommands.NewHorizontalTabGroup
                    || e.Command == DocumentPaneCommands.NewVerticalTabGroup
                    || e.Command == DocumentPaneCommands.CloseAllButThis)
                {
                    if (Items.Count <= 1)
                    {
                        e.CanExecute = false;
                    }
                }
            }
        }

        /// <summary>
        /// Provides derived classes an opportunity to handle changes to the ContainsActiveDocument property.
        /// </summary>
        protected virtual void OnContainsActiveDocumentChanged(DependencyPropertyChangedEventArgs e)
        {
            NotifyPropertyChanged("ContainsActiveDocument");
        }

        protected override void OnExecuteCommand(object sender, ExecutedRoutedEventArgs e)
        {
            if (e.Command == DocumentPaneCommands.CloseThis)
            {
                CloseThis(e.Parameter as ManagedContent);
                e.Handled = true;
            }
            else if (e.Command == DocumentPaneCommands.CloseAllButThis)
            {
                CloseAllButThis();
                e.Handled = true;
            }
            else if (e.Command == DocumentPaneCommands.NewHorizontalTabGroup)
            {
                CreateNewHorizontalTabGroup();
                e.Handled = true;
            }
            else if (e.Command == DocumentPaneCommands.NewVerticalTabGroup)
            {
                CreateNewVerticalTabGroup();
                e.Handled = true;
            }
        }

        protected override void OnInitialized(EventArgs e)
        {
            CommandBindings.Add(
                new CommandBinding(DocumentPaneCommands.CloseThis, OnExecuteCommand, OnCanExecuteCommand));
            CommandBindings.Add(
                new CommandBinding(DocumentPaneCommands.CloseAllButThis, OnExecuteCommand, OnCanExecuteCommand));
            CommandBindings.Add(
                new CommandBinding(DocumentPaneCommands.NewHorizontalTabGroup, OnExecuteCommand, OnCanExecuteCommand));
            CommandBindings.Add(
                new CommandBinding(DocumentPaneCommands.NewVerticalTabGroup, OnExecuteCommand, OnCanExecuteCommand));

            base.OnInitialized(e);
        }

        protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (object newItem in e.NewItems)
                {
                    if (!(newItem is DockableContent) && !(newItem is DocumentContent))
                    {
                        throw new InvalidOperationException(
                            "DocumentPane can contain only DockableContents or DocumentContents!");
                    }

                    if (newItem is DockableContent
                        && (((DockableContent)newItem).DockableStyle & DockableStyle.Document) == 0)
                    {
                        ((DockableContent)newItem).DockableStyle |= DockableStyle.Document;
                    }

                    var dockContent = newItem as DockableContent;
                    if (dockContent != null)
                    {
                        dockContent.SetStateToDocument();
                    }
                }
            }

            RefreshContainsActiveDocumentProperty();

            foreach (var dp in GetDependencyProperties(this))
            {
                Debug.WriteLine(string.Format("{0} - {1}", dp.Name, dp.GlobalIndex));
            }
            foreach (var dp in GetAttachedProperties(this))
            {
                Debug.WriteLine(string.Format("{0} - {1}", dp.Name, dp.GlobalIndex));
            }
            base.OnItemsChanged(e);
        }

        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            base.OnMouseDown(e);

            if (!e.Handled)
            {
                if (_partHeader != null && !_partHeader.IsMouseOver)
                {
                    //prevent document content to start dragging when it is clicked outside of the header area
                    e.Handled = true;
                }
            }
        }

        protected override void OnSelectionChanged(SelectionChangedEventArgs e)
        {
            var selectedContent = SelectedItem as ManagedContent;
            if (selectedContent != null && GetManager() != null)
            {
                GetManager().ActiveDocument = selectedContent;
            }

            base.OnSelectionChanged(e);
        }

        /// <summary>
        /// Provides a secure method for setting the ContainsActiveDocument property.  
        /// This dependency property indicates if this pane contains a <see cref="DocumentPane"/> that has <see cref="ManagedContent.IsActiveDocument"/> property set to true.
        /// </summary>
        /// <param name="value">The new value for the property.</param>
        protected void SetContainsActiveDocument(bool value)
        {
            SetValue(ContainsActiveDocumentPropertyKey, value);
        }

        /// <summary>
        /// Handles changes to the ContainsActiveDocument property.
        /// </summary>
        private static void OnContainsActiveDocumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DocumentPane)d).OnContainsActiveDocumentChanged(e);
        }

        private void CloseAllButThis()
        {
            var activeContent = GetManager().ActiveDocument as DocumentContent;
            foreach (DocumentContent cnt in GetManager().Documents.ToArray())
            {
                if (cnt != activeContent)
                {
                    cnt.Close();
                }
            }
        }

        private void CloseThis(ManagedContent contentToClose)
        {
            if (GetManager() == null)
            {
                return;
            }

            if (contentToClose == null)
            {
                contentToClose = SelectedItem as ManagedContent;
            }

            var dockableContent = contentToClose as DockableContent;

            if (dockableContent != null)
            {
                dockableContent.Close();
            }
            else
            {
                var documentContent = contentToClose as DocumentContent;
                if (documentContent != null)
                {
                    documentContent.Close();
                }
            }
        }

        private void DocumentPane_Loaded(object sender, RoutedEventArgs e)
        {
            if (Parent == null)
            {
                return;
            }

            if (GetManager() == null)
            {
                return;
            }
            //throw new InvalidOperationException("DocumentPane must be put under a DockingManager!");

            //try to set this as main document pane
            if (GetManager().MainDocumentPane == null)
            {
                GetManager().MainDocumentPane = this;
                NotifyPropertyChanged("IsMainDocumentPane");
            }
            else
            {
                //or ensure that this document pane is under or at the same level of the MainDocumentPane
                GetManager().EnsurePanePositionIsValid(this);
            }
        }

        private void OnPreviewDrop(object sender, DragEventArgs e)
        {
            GetManager().MainDocumentPane = this;
        }

        private void ShowDocumentsListMenu()
        {
            if (Items.Count == 0)
            {
                return; //nothings to show
            }

            var cxMenuDocuments = (ContextMenu)TryFindResource("DocumentsListMenu");
            if (cxMenuDocuments != null)
            {
                cxMenuDocuments.Items.Clear();
                Items.OfType<ManagedContent>().OrderBy(c => c.Title).ForEach(
                    c => cxMenuDocuments.Items.Add(
                        new MenuItem
                            {
                                Header = c.Title,
                                Command = ManagedContentCommands.Activate,
                                CommandTarget = c,
                                Icon = new Image
                                    {
                                        Source = c.Icon,
                                        Width = 16,
#if NET4
                                        UseLayoutRounding = true
#endif
                                    }
                            }));           

                if (_optionsContextMenuPlacementTarget != null)
                {
                    cxMenuDocuments.Placement = PlacementMode.Bottom;
                    cxMenuDocuments.PlacementTarget = _optionsContextMenuPlacementTarget;
                }
                else
                {
                    cxMenuDocuments.Placement = PlacementMode.MousePoint;
                    cxMenuDocuments.PlacementTarget = this;
                }

                cxMenuDocuments.IsOpen = true;
                cxMenuDocuments.Closed += cxMenuDocuments_Closed;
            }
        }

        /// <summary>
        /// Clear context menu that shows documents list
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cxMenuDocuments_Closed(object sender, RoutedEventArgs e)
        {
            var cxMenuDocuments = (ContextMenu)sender;
            cxMenuDocuments.Items.Clear();
            cxMenuDocuments.Closed -= cxMenuDocuments_Closed;
        }

        #endregion
    }
}